/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.pooled;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

/**
 * 同步的线程安全的数据库连接池
 * This is a simple, synchronous, thread-safe database connection pool.
 *
 * @author Clinton Begin
 */
public class PooledDataSource implements DataSource {

  //日志
  private static final Log log = LogFactory.getLog(PooledDataSource.class);
  //连接池状态
  private final PoolState state = new PoolState(this);
  //非池化数据源,一个池型数据源就是一个非池型数据源加上一个连接池，也就是说，池型数据源是在非池型数据源基础上扩展而来
  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  //连接池中最多可拥有的活动连接数
  protected int poolMaximumActiveConnections = 10;
  //连接池中最多可拥有的空闲连接数
  protected int poolMaximumIdleConnections = 5;
  //连接池最大检出时间，如果一个连接验证时间超过设定值，则将这个连接设置为过期（发生在推出连接时）
  protected int poolMaximumCheckoutTime = 20000;
  //池等待时间，当需要从池中获取一个连接时，如果空闲连接数量为0，而活动连接的数量也达到了最大值，
  //那么就针对那个最早取出的连接进行检查验证（checkout），如果验证成功（即在上面poolMaximumCheckoutTime限定的时间内验证通过），
  //说明这个连接还处于使用状态，这时取出操作暂停，线程等待限定时间，这个限定时间就是这个参数的使用位置。
  protected int poolTimeToWait = 20000;
  //这是一个关于坏连接容忍度的底层设置， 作用于每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接，那么这个数据源允许这 
  //个线程尝试重新获取一个新的连接，但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。
  //默认值：3 (Since: 3.4.5)
  protected int poolMaximumLocalBadConnectionTolerance = 3;
  // 发送到数据库的侦测查询，用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”，
  //这会导致多数数据库驱动失败时带有一个恰当的错误消息。
  protected String poolPingQuery = "NO PING QUERY SET";
  //是否启用侦测查询。若开启，也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性（最好是一个非常快的 SQL），默认值：false。
  protected boolean poolPingEnabled;
  //配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间，来避免不必要的侦测，默认值：0
  //（即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用）。
  protected int poolPingConnectionsNotUsedFor;
  //连接的类型编码，这个类型编码在创建池型数据源实例的时候会被组装，他的组装需要从数据源中获取连接的url、username、password三个值，
  //将其按顺序组合在一起，这个类型编码可用于区别连接种类。
  private int expectedConnectionTypeCode;
  /**
   * 无参构造器，内部的非池化数据源，通过新建一个UnpooledDataSource实现。
   */
  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }
  /**
   * 有参构造器，通过传入非池化UnpooledDataSource实例进行构造池化数据源
   * @param dataSource
   */
  public PooledDataSource(UnpooledDataSource dataSource) {
    this.dataSource = dataSource;
  }
  /**
   * 通过构建非池化参数构建，同时生成连接的类型编码expectedConnectionTypeCode。
   * @param driver
   * @param url
   * @param username
   * @param password
   */
  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }
  /**
   * 通过构建非池化参数构建，同时生成连接的类型编码expectedConnectionTypeCode。
   * @param driver
   * @param url
   * @param username
   * @param password
   */
  public PooledDataSource(String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }
  /**
   * 通过构建非池化参数构建，同时生成连接的类型编码expectedConnectionTypeCode。
   * @param driver
   * @param url
   * @param username
   * @param password
   */
  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }
  /**
   * 通过构建非池化参数构建，同时生成连接的类型编码expectedConnectionTypeCode。
   * @param driver
   * @param url
   * @param username
   * @param password
   */
  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }
  /**
   * 覆盖了DataSource.getConnection方法，每次都是pop一个Connection，即从池中取出一个来
   */
  @Override
  public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }
  /**
   * 覆盖了DataSource.getConnection方法，每次都是pop一个Connection，即从池中取出一个来
   */
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }
  /**
   * 设置连接的最大超时时间
   */
  @Override
  public void setLoginTimeout(int loginTimeout) throws SQLException {
    DriverManager.setLoginTimeout(loginTimeout);
  }
  /**
   * 获取连接的最大超时时间
   */
  @Override
  public int getLoginTimeout() throws SQLException {
    return DriverManager.getLoginTimeout();
  }
  /**
   * 设置日志打印的PrintWriter
   */
  @Override
  public void setLogWriter(PrintWriter logWriter) throws SQLException {
    DriverManager.setLogWriter(logWriter);
  }
  /**
   * 获取日志打印的PrintWriter
   */
  @Override
  public PrintWriter getLogWriter() throws SQLException {
    return DriverManager.getLogWriter();
  }
  /**
   * 设置连接的驱动，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setDriver(String driver) {
    dataSource.setDriver(driver);
    forceCloseAll();
  }
  /**
   * 设置连接的url，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setUrl(String url) {
    dataSource.setUrl(url);
    forceCloseAll();
  }
  /**
   * 设置连接的username，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setUsername(String username) {
    dataSource.setUsername(username);
    forceCloseAll();
  }
  /**
   * 设置连接的password，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setPassword(String password) {
    dataSource.setPassword(password);
    forceCloseAll();
  }
  /**
   * 设置连接是否自动提交，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setDefaultAutoCommit(boolean defaultAutoCommit) {
    dataSource.setAutoCommit(defaultAutoCommit);
    forceCloseAll();
  }
  /**
   * 设置连接的事务隔离级别，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
    forceCloseAll();
  }
  /**
   * 设置连接的驱动的各类属性，关闭所有活动或空闲的链接
   * @param driver
   */
  public void setDriverProperties(Properties driverProps) {
    dataSource.setDriverProperties(driverProps);
    forceCloseAll();
  }

  /*
   * 设置连接池中最多可拥有的活动连接数
   * The maximum number of active connections
   *
   * @param poolMaximumActiveConnections The maximum number of active connections
   */
  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    forceCloseAll();
  }

  /*
   * 设置连接池中最多可拥有的空闲连接数
   * The maximum number of idle connections
   *
   * @param poolMaximumIdleConnections The maximum number of idle connections
   */
  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    forceCloseAll();
  }

  /*
   * 设置坏连接容忍数
   * The maximum number of tolerance for bad connection happens in one thread
    * which are applying for new {@link PooledConnection}
   *
   * @param poolMaximumLocalBadConnectionTolerance
   * max tolerance for bad connection happens in one thread
   *
   * @since 3.4.5
   */
  public void setPoolMaximumLocalBadConnectionTolerance(
      int poolMaximumLocalBadConnectionTolerance) {
    this.poolMaximumLocalBadConnectionTolerance = poolMaximumLocalBadConnectionTolerance;
  }

  /*
   * 设置连接池最大检出时间
   * The maximum time a connection can be used before it *may* be
   * given away again.
   *
   * @param poolMaximumCheckoutTime The maximum time
   */
  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
    forceCloseAll();
  }

  /*
   * 设置连接池等待时间
   * The time to wait before retrying to get a connection
   *
   * @param poolTimeToWait The time to wait
   */
  public void setPoolTimeToWait(int poolTimeToWait) {
    this.poolTimeToWait = poolTimeToWait;
    forceCloseAll();
  }

  /*
   * 设置数据库的侦测查询语句
   * The query to be used to check a connection
   *
   * @param poolPingQuery The query
   */
  public void setPoolPingQuery(String poolPingQuery) {
    this.poolPingQuery = poolPingQuery;
    forceCloseAll();
  }

  /*
   * 设置是否开启侦探查询
   * Determines if the ping query should be used.
   *
   * @param poolPingEnabled True if we need to check a connection before using it
   */
  public void setPoolPingEnabled(boolean poolPingEnabled) {
    this.poolPingEnabled = poolPingEnabled;
    forceCloseAll();
  }

  /*
   * 设置配置 poolPingQuery 的使用频度
   * If a connection has not been used in this many milliseconds, ping the
   * database to make sure the connection is still good.
   *
   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
   */
  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
    this.poolPingConnectionsNotUsedFor = milliseconds;
    forceCloseAll();
  }
  /**
   * 获取数据库连接驱动
   * @return
   */
  public String getDriver() {
    return dataSource.getDriver();
  }
  /**
   * 获取数据库连接RUL
   * @return
   */
  public String getUrl() {
    return dataSource.getUrl();
  }
  /**
   * 获取数据库连接username
   * @return
   */
  public String getUsername() {
    return dataSource.getUsername();
  }
  /**
   * 获取数据库连接password
   * @return
   */
  public String getPassword() {
    return dataSource.getPassword();
  }
  /**
   * 获取数据库连接是否自动提交
   * @return
   */
  public boolean isAutoCommit() {
    return dataSource.isAutoCommit();
  }
  /**
   * 获取数据库连接事务驱动级别
   * @return
   */
  public Integer getDefaultTransactionIsolationLevel() {
    return dataSource.getDefaultTransactionIsolationLevel();
  }
  /**
   * 获取数据库连接驱动的属性
   * @return
   */
  public Properties getDriverProperties() {
    return dataSource.getDriverProperties();
  }
  /**
   * 获取池化数据源连接最大活动数
   * @return
   */
  public int getPoolMaximumActiveConnections() {
    return poolMaximumActiveConnections;
  }
  /**
   * 获取池化数据源连接最大空闲数
   * @return
   */
  public int getPoolMaximumIdleConnections() {
    return poolMaximumIdleConnections;
  }
  /**
   * 获取池化数据源连接最大坏连接容忍数
   * @return
   */
  public int getPoolMaximumLocalBadConnectionTolerance() {
    return poolMaximumLocalBadConnectionTolerance;
  }
  /**
   * 获取池化数据源连接最大检出试卷
   * @return
   */
  public int getPoolMaximumCheckoutTime() {
    return poolMaximumCheckoutTime;
  }
  /**
   * 获取池化数据源连接最大等待时间
   * @return
   */
  public int getPoolTimeToWait() {
    return poolTimeToWait;
  }
  /**
   * 获取池化数据源连接侦探校验语句
   * @return
   */
  public String getPoolPingQuery() {
    return poolPingQuery;
  }
  /**
   * 获取池化数据源连接侦探检验是否开启
   * @return
   */
  public boolean isPoolPingEnabled() {
    return poolPingEnabled;
  }
  /**
   * 获取 poolPingQuery 的使用频度
   * @return
   */
  public int getPoolPingConnectionsNotUsedFor() {
    return poolPingConnectionsNotUsedFor;
  }

  /*
   * 关闭所有的activeConnections和idleConnections
   * 使用场景：连接是在数据源完全设置完整的情况下才生成的，数据源就是连接生成的基础，
   * 当我们要修改数据源的基础属性的时候，原有设置上产生的连接必定不再适合新的设置，需要全部推倒重来。
   * Closes all active and idle connections in the pool
   */
  public void forceCloseAll() {
    synchronized (state) {
      expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      for (int i = state.activeConnections.size(); i > 0; i--) {//关闭所有活动的连接
        try {
          PooledConnection conn = state.activeConnections.remove(i - 1);
          conn.invalidate();

          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
      for (int i = state.idleConnections.size(); i > 0; i--) {//关闭所有空闲的连接
        try {
          PooledConnection conn = state.idleConnections.remove(i - 1);
          conn.invalidate();

          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource forcefully closed/removed all connections.");
    }
  }

  public PoolState getPoolState() {
    return state;
  }
  /**
   * 根据链接、用户名、密码构建连接的类型编码
   * @param url
   * @param username
   * @param password
   * @return
   */
  private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }
  /**
   *  (1) 这个方法同样是一个同步方法，拥有同步锁，以池状态实例为锁。
　　*　(2) 首先我们将当前要推入的连接实例从活动连接中删除，表示其不再处于使用状态。
　　*　(3) 然后对连接额可用性进行（valid）判断，如果还处于可用状态，则验证空闲连接集合中的空闲连接数量是否小于设置的限定值（poolMaximumIdleConnections）和当前连接实例的类型编码是否与当前池型数据源中的连接类型编码一致，如果上面两点都满足，则进行下一步：
　　*　(4) 新建一个池型连接实例并将其添加到空闲连接集合中，这个池型连接实例是以之前要推入的连接为基础重新创建的，也就是说是针对那个要推入的池型连接的真实连接重新创建一个池型连接代理（只改变外包装，实质不改变），并将原池型连接的时间戳设置统统设置到新的连接中，保持连接的持续性，然后将原池型连接置为无效。
　　*　(5) 然后唤醒所有沉睡线程notifyAll()。
　　*　(6) 如果第(3)点中的判断中有一个不成立（空闲连接数量达到最大值或者连接的类型编码不一致）那么直接将该连接的真实连接关闭，池连接置为无效即可。
   * @param conn
   * @throws SQLException
   */
  protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      //活动连接队列中移除该连接
      state.activeConnections.remove(conn);
      if (conn.isValid()) {//连接有效时
    	//当前空闲连接队列中连接数量小于最大空闲连接数且连接的类型编码符合要求
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          //累计连接检出时间增加
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {//回滚连接
            conn.getRealConnection().rollback();
          }
          //重新欢迎该连接，并添加到空闲连接队列中
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          state.notifyAll();
        } else {//其他情况，该连接需要真正的被关闭
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {//回滚
            conn.getRealConnection().rollback();
          }
          //真正的关闭连接
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {//无效连接时，抛出异常，坏连接数加一
        if (log.isDebugEnabled()) {
          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    }
  }
  /**
   * 弹出池化连接
   *  (1) 这是个同步方法，线程安全。但是将synchronized锁同步放置到循环内部，而不是循环之外的原因是因为：如果将同步锁放置在循环之外，当多个线程执行到锁的位置，其中一个线程获得锁然后开始执行循环，如果发生问题导致无限循环，那么这个锁将是一直被这个线程所持有，导致其他线程永久处于等待锁的状态，程序无法执行下去。而将锁放置到循环内部，当多个线程来到锁之前，其中一个线程获得锁，执行循环内部代码，当执行完成一次循环，无论成功失败，都会释放锁，而其他线程就可以获得锁进而执行。
　　*　 (2) 首先验证空闲连接集合是否为空（验证是否还有空闲连接备用），如果存在空闲连接，那么直接获取这个空闲连接，将这个连接从空闲连接集合中删除。
　　*　 (3) 如果没有空闲连接，那么就验证活动连接集合中连接的数量是否达到最大值（poolMaximumActiveConnections），如果未达到最大值，这时，我们可以直接创建一个新的池型连接（需要一个真实连接于与一个池型数据源实例作为参数）
　　*　 (4) 如果活动连接集合中的连接数目已经达到最大值（poolMaximumActiveConnections），那么就针对最早的那个活动连接（即在集合中排在0位的那个连接实例）进行验证。并获取其验证时间间隔值（该连接上一次记录验证时间戳到当前时间的间隔），将其与池连接的最大验证时限（poolMaximumCheckoutTime）进行比较，如果前者大，说明针对这个连接距上一次记录验证时间戳的时间超过了限定时限，这时将这个老连接从活动连接集合中删除，并新建一个池连接，还以老连接所代理的真实连接为真实连接（实际上就是创建一个新的代理），并将老的池连接设为无效。
　　*　 (5) 如果验证时间与显示时间比较结果为验证时间小于限定时限（这个限定时限的设置需要根据项目实际情况来设置，或通过经验来设置，确保在这个时间之内连接的数据库操作执行完毕，不然贸然将连接关闭会导致原本的数据库操作失败），说明这个连接还可能处于使用状态，这时候只有等待一途，这里将线程设置等待限定秒数(poolTimeToWait)，线程进入等待状态，那么就会释放同步锁，此时其他线程就能获得锁来进行执行。当前线程在等待N秒之后自动进入准备状态准备重新获得锁。
　　*　 (6) 然后就获得的连接进行判断，如果连接不为空，那么验证连接是否可用（isValid），如果连接可用则设置连接类型编码，并记录验证时间戳（setCheckoutTimestamp）与最后一次使用时间戳(setLastUsedTimestamp)，这两个时间戳可用于计算该连接的验证时间与最后一次使用时间，在前面会使用到这些值进行判断。再然后将该链接添加到活动连接集合中。
　　*　 (7)如果获取的连接为空，或者说没有获取到连接，则坏连接数加1，将连接置null，并验证坏连接数值，如果比当前空闲连接数量+3都大的话，那么就放弃获取连接，并抛出SqlException，（抛出异常也就意味着执行的终止，这个线程将不再执行循环操作）
   * @param username
   * @param password
   * @return
   * @throws SQLException
   */
  private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;
    //最外面是while死循环，如果一直拿不到connection，则不断尝试
    while (conn == null) {
      //这是个同步方法，线程安全。但是将synchronized锁同步放置到循环内部，而不是循环之外的原因是因为：
    	//如果将同步锁放置在循环之外，当多个线程执行到锁的位置，其中一个线程获得锁然后开始执行循环，如果发生问题导致无限循环，
    	//那么这个锁将是一直被这个线程所持有，导致其他线程永久处于等待锁的状态，程序无法执行下去。而将锁放置到循环内部，
    	//当多个线程来到锁之前，其中一个线程获得锁，
    	//执行循环内部代码，当执行完成一次循环，无论成功失败，都会释放锁，而其他线程就可以获得锁进而执行。
      synchronized (state) {
        if (!state.idleConnections.isEmpty()) {//如果有空闲的连接的话,删除空闲列表里第一个，并返回
          // Pool has available connection
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {//如果没有空闲的连接
          // Pool does not have available connection
          //如果当前活动连接数小于最大活动连接，就创建一个池化连接
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {//如果当前活动连接数大于最大活动连接，暂时不能创建活动连接，需要等待
            // Cannot create new connection
        	//获取最早创建的活动连接
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            //获取该连接的检出时间
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {//当该连接的检出时间大于设定的最大检出时间时
              // Can claim overdue connection
              //过期连接数加一
              state.claimedOverdueConnectionCount++;
              //累计过期连接检出时间添加当前连接的检出时间
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              //总检出时间添加当前连接的检出时间
              state.accumulatedCheckoutTime += longestCheckoutTime;
              //从活动连接队列中移除当前活动连接
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {//如果当前连接是非自动提交的配置，需要手动进行回滚。
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {
                  /*
                     Just log a message for debug and continue to execute the following
                     statement like nothing happend.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not intterupt current executing thread and give current thread a
                     chance to join the next competion for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }  
              }
              //根据原有连接，创建一个新的连接
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              //设置连接创建时间（使用原来连接的时间）
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
             //设置连接最后使用时间（使用原来连接的时间）
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              //设置原来连接无效
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {//当该连接的检出时间小于设定的最大检出时间时，就必须等待
              // Must wait
              try {
                if (!countedWait) {
                  //等待连接数加一
                  state.hadToWaitCount++;
                  //表示当前连接已经在等待中
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);
                //总等待时间累加
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {
          // ping to server and check the connection is valid or not
          if (conn.isValid()) {//连接有效时
            if (!conn.getRealConnection().getAutoCommit()) {
              //连接是非自动提交时，把连接进行回滚操作
              conn.getRealConnection().rollback();
            }
            //设置编码类型
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            //设置连接检出时间
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            //设置连接最后使用实际
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn);
            //请求次数加一
            state.requestCount++;
            //请求总时间累加
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {//连接无效时
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
            }
            //坏连接数加一
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            //当坏连接次数大于 空闲连接数和坏连接容忍设置数时，抛出异常
            if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }

    }

    if (conn == null) {//当获取不到连接时，抛出异常。
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }

  /**
   * 检查连接是否可用
   * 该方法会被池连接PooledConnection类中的isValid()方法调用，用于判断一个连接是否还可用。这个方法就是真正用于判断连接可用与否的功能性方法。
	 * 　(1) 首先创建一个局部变量result用于保存判断结果，默认为true
	*　　(2) 然后将当前池型连接包裹的真实连接的开闭状态值的非值赋值给result（当真实连接处于关闭状态时，result值为false，当真实连接处于开启状态时，result值为true），如果赋值过程出现了异常，则直接将result置false
	*　　(3) 判断result的值，如果result值为true，则判断poolPingEnabled的值，这是侦测查询的开关，如果这个值为true，表示开启侦测查询，那么就可以执行以下内容。
	*　　(4) 判断poolPingConnectionsNotUsedFor的值是否大于等于0（这个判断的意思是判断是否设置了正确的poolPingConnectionsNotUsedFor值），并且判断该连接的自最后一次使用以来的时间间隔是否大于设定的poolPingConnectionsNotUsedFor值（验证该连接是否到了需要进行侦测查询的时间，如果小于设置时间则不进行侦测查询）
	*　　(5) 如果上述条件均满足，则进行一次侦测查询，这个侦测查询就是针对这个连接的一个测试查询，看看整个查询过程是否通畅，若通畅（没有任何异常出现），则将result置为true，一旦测试过程出现了异常，则将该连接的真实连接关闭，并将result置为false
   * Method to check to see if a connection is still usable
   *
   * @param conn - the connection to check
   * @return True if the connection is still usable
   */
  protected boolean pingConnection(PooledConnection conn) {
	//连接是否可用的标识
    boolean result = true;

    try {
      //连接是否被关闭，关闭则表示连接不可用，否则，可用
      result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {//发送异常，说明不可用
      if (log.isDebugEnabled()) {
        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
      }
      result = false;
    }

    if (result) {
      if (poolPingEnabled) {//如果开启连接可用侦查
    	//侦探检查评率在范围内时
        if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
          try {
            if (log.isDebugEnabled()) {
              log.debug("Testing connection " + conn.getRealHashCode() + " ...");
            }
            //构建进行侦探的sql语句,如果测试正常，说明连接可用
            Connection realConn = conn.getRealConnection();
            Statement statement = realConn.createStatement();
            ResultSet rs = statement.executeQuery(poolPingQuery);
            rs.close();
            statement.close();
            if (!realConn.getAutoCommit()) {
              realConn.rollback();
            }
            result = true;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
            }
          } catch (Exception e) {//测试连接异常时，说明连接不可用
            log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
            try {
              conn.getRealConnection().close();
            } catch (Exception e2) {
              //ignore
            }
            result = false;
            if (log.isDebugEnabled()) {
              log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
            }
          }
        }
      }
    }
    return result;
  }

  /*
   * 获取代理连接的真实连接
   * Unwraps a pooled connection to get to the 'real' connection
   *
   * @param conn - the pooled connection to unwrap
   * @return The 'real' connection
   */
  public static Connection unwrapConnection(Connection conn) {
    if (Proxy.isProxyClass(conn.getClass())) {
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  @Override
protected void finalize() throws Throwable {
    forceCloseAll();
    super.finalize();
  }

  @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  @Override
public Logger getParentLogger() {
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // requires JDK version 1.6
  }

}
